package com.wuyanzu.shixicoderedo.service.impl;

import com.wuyanzu.shixicoderedo.common.TaskAction;
import com.wuyanzu.shixicoderedo.common.dto.FeeItemDTO;
import com.wuyanzu.shixicoderedo.common.entity.*;
import com.wuyanzu.shixicoderedo.common.threadlocal.RequestContext;
import com.wuyanzu.shixicoderedo.common.threadpool.ExecutorManagerWithContext;
import com.wuyanzu.shixicoderedo.mapper.SettlementMapper;
import com.wuyanzu.shixicoderedo.service.SettlementService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ExecutionException;


@Service
@Slf4j
public class SettlementServiceImpl implements SettlementService {

    // 注入 Mapper，用于数据库操作
    @Autowired
    private SettlementMapper settlementMapper;

    // 处理住院结算请求
    @Override
    public Map<String, Object> handleSettlement(Long patientId, List<FeeItemDTO> items) {

        // 创建 SettlementRequest 实例用于记录请求
        SettlementRequest req = new SettlementRequest();

        // 设置患者 ID
        req.setPatientId(patientId);

        // 将请求数据（费用项列表）存入 reqData
        req.setReqData(Map.of("items", items));

        // 设置请求状态为 PENDING（待处理）
        req.setStatus("PENDING");

        // 调用 Mapper 保存请求记录，并回填生成的主键 ID
        settlementMapper.insertSettlementRequest(req);

        // 获取回填的 requestId
        Long requestId = req.getId();

        // 生成唯一 correlationId，用于日志链路追踪
        String correlationId = UUID.randomUUID().toString();

        // 创建 RequestContext，存放 correlationId 与 patientId
        RequestContext ctx = new RequestContext(correlationId, String.valueOf(patientId));

        // 构造社保确权任务
        TaskAction<Boolean> socSecTask = () -> {
            // 查询患者最新社保记录
            SocialSecurityRecord rec = settlementMapper.findLatestSocialByPatient(patientId);

            // 判断是否在保（1=在保，0=不在保）
            boolean ok = rec != null && rec.getInsured() != null && rec.getInsured() == 1;

            // 记录日志
            log.info("[{}] 社保确权 patientId={}, ok={}", correlationId, patientId, ok);

            // 返回校验结果
            return ok;
        };

        // 构造三要素校验任务
        TaskAction<Boolean> threeTask = () -> {
            // 查询患者信息
            Patient p = settlementMapper.findPatientById(patientId);

            // 若未查询到患者，则校验不通过
            if (p == null) return false;

            // 简单示例：认为三要素校验通过
            boolean ok = true;

            // 记录日志
            log.info("[{}] 三要素校验 patientId={}, ok={}", correlationId, patientId, ok);

            // 返回校验结果
            return ok;
        };

        // 构造费用预校验任务
        TaskAction<Boolean> feeTask = () -> {
            // 若费用项为空，则校验不通过
            if (items == null || items.isEmpty()) return false;

            // 遍历每个费用项进行校验
            for (FeeItemDTO it : items) {
                // 查询费用项主数据
                FeeItemMaster master = settlementMapper.findFeeItemByCode(it.getCode());

                // 若费用项不存在，则校验不通过
                if (master == null) {
                    log.warn("[{}] 费用项不存在 code={}", correlationId, it.getCode());
                    return false;
                }

                // 获取提交金额，空则视为 0
                BigDecimal amt = it.getAmount() == null ? BigDecimal.ZERO : it.getAmount();

                // 若金额超出允许金额，则校验不通过
                if (amt.compareTo(master.getAllowedAmount()) > 0) {
                    log.warn("[{}] 费用超限 code={}, amt={}, allowed={}", correlationId, it.getCode(), amt, master.getAllowedAmount());
                    return false;
                }
            }

            // 费用校验通过
            log.info("[{}] 费用预校验通过 patientId={}", correlationId, patientId);

            // 返回校验结果
            return true;
        };

        // 构造风险评分任务
        TaskAction<Double> riskTask = () -> {
            // 初始化风险分数
            double score = 0.0;

            // 查询最新社保记录
            SocialSecurityRecord rec = settlementMapper.findLatestSocialByPatient(patientId);

            // 判断是否在保，不在保增加风险分数
            boolean insured = rec != null && rec.getInsured() != null && rec.getInsured() == 1;
            if (!insured) score += 0.4;

            // 遍历费用项，金额过大增加风险分数
            for (FeeItemDTO it : items) {
                double amt = it.getAmount() == null ? 0.0 : it.getAmount().doubleValue();
                if (amt > 10000) score += 0.5;
            }

            // 限制最大分数为 1.0
            if (score > 1.0) score = 1.0;

            // 记录日志
            log.info("[{}] 风险评分 patientId={}, score={}", correlationId, patientId, score);

            // 返回风险分数
            return score;
        };

        // 执行四个任务并发计算
        List<Object> results;
        try {
            // 获取线程池管理器单例
            ExecutorManagerWithContext mgr = ExecutorManagerWithContext.getInstance();

            // 执行任务并返回结果列表
            results = mgr.executeTasks(ctx, socSecTask, threeTask, feeTask, riskTask);
        } catch (InterruptedException ie) {
            // 若线程被中断，恢复中断状态
            Thread.currentThread().interrupt();

            // 抛出运行时异常
            throw new RuntimeException("interrupted", ie);
        } catch (ExecutionException ee) {
            // 若任务执行失败，抛出原始异常
            throw new RuntimeException("task execution failed", ee.getCause());
        }

        // 解析执行结果
        boolean socsecOk = results.size() > 0 && Boolean.TRUE.equals(results.get(0));
        boolean threeOk = results.size() > 1 && Boolean.TRUE.equals(results.get(1));
        boolean feeOk = results.size() > 2 && Boolean.TRUE.equals(results.get(2));
        double riskScore = 0.0;
        if (results.size() > 3 && results.get(3) instanceof Double) {
            riskScore = (Double) results.get(3);
        }

        // 创建 SettlementResult 实例
        SettlementResult r = new SettlementResult();

        // 设置关联请求 ID
        r.setRequestId(requestId);

        // 设置社保、三要素、费用校验结果
        r.setSocsecOk(socsecOk ? 1 : 0);
        r.setThreeOk(threeOk ? 1 : 0);
        r.setFeeOk(feeOk ? 1 : 0);

        // 设置风险评分
        r.setRiskScore(riskScore);

        // 构造详细结果 Map
        Map<String, Object> detail = new HashMap<>();
        detail.put("socsecOk", socsecOk);
        detail.put("threeOk", threeOk);
        detail.put("feeOk", feeOk);
        detail.put("riskScore", riskScore);

        // 设置详细结果
        r.setDetail(detail);

        // 保存结算结果到数据库
        settlementMapper.insertSettlementResult(r);

        // 构造返回响应
        Map<String, Object> resp = new HashMap<>();
        resp.put("socsecOk", socsecOk);
        resp.put("threeOk", threeOk);
        resp.put("feeOk", feeOk);
        resp.put("riskScore", riskScore);
        resp.put("detail", detail);

        // 返回聚合响应
        return resp;
    }
}
